#pragma once
#include<string>
using std::string;

#include<map>
using std::map;

#include<vector>
using std::vector;

#include "SGD Wrappers\CSGD_TextureManager.h"
#include "SGD Wrappers\SGD_Math.h"
#include "Tile.h"
#include "tinyxml\tinyxml.h"
#include "SGD Wrappers\CSGD_Direct3D.h"

class ParticleManager
{
private:
	ParticleManager();
	~ParticleManager();
	ParticleManager(const ParticleManager& PM);
	ParticleManager& operator=(ParticleManager& PM);

	static ParticleManager* Instance;

	class Emitter
	{
	private:
		struct Particle
		{
		public:
			float	PosX;
			float	PosY;
			float	CurLife;
			float	CurVelX;
			float	CurVelY;
			D3DXCOLOR  CurColor;
			float	CurScaleX;
			float	CurScaleY;
			bool	isAlive;
			bool	haslife;
			bool	JustSpawned;

			Particle()
			{
				PosX = 0;
				PosY = 0;
				CurLife = 0;
				CurVelX = 0;
				CurVelY = 0;
				CurColor = D3DXCOLOR(0,0,0,0);
				CurScaleX = 0;
				CurScaleY = 0;
				isAlive = false;
				haslife = false;
				JustSpawned = false;
			}
		};

		Particle**		Particles;
		RECT			EmitterRect;
		int				ImageID;
		int				SourceBlend;
		int				DestBlend;
		float			MaxLife;
		float			MinLife;
		bool			looping;
		D3DXCOLOR		StartColor;
		D3DXCOLOR		EndColor;
		float			MaxParticles;
		float			ScaleX;
		float			ScaleY;
		float			VelX;
		float			VelY;
		float			EndScaleX;
		float			EndScaleY;
		float			EndVelX;
		float			EndVelY;
		float			TimeBucket;

	public:
		Emitter()
		{
			EmitterRect.top = 0;
			EmitterRect.bottom = 0;
			EmitterRect.left = 0;
			EmitterRect.right = 0;
			Particles = nullptr;
			ImageID = -1;
			SourceBlend = 0;
			DestBlend = 0;
			MaxLife = 0.0f; 
			MinLife = 0.0f;
			looping = false;
			StartColor = D3DXCOLOR(0, 0, 0, 0);
			EndColor = D3DXCOLOR(255, 255, 255, 255);
			MaxParticles = 0;
			ScaleX = 0.0f;
			ScaleY = 0.0f;
			VelX = 0.0f;
			VelY = 0.0f;
			EndVelX = 0.0f;
			EndVelY = 0.0f;
			EndScaleX = 0.0f;
			EndScaleY = 0.0f;
			TimeBucket = 0.0f;
		}
		~Emitter() 
		{
			
		}

		void Render(void)  
		{
			DWORD temp[2];
			temp[0] = 0;
			temp[1] = 0;

			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->GetRenderState(D3DRS_SRCBLEND, &temp[0]);
			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->GetRenderState(D3DRS_DESTBLEND, &temp[1]);

			CSGD_Direct3D::GetInstance()->GetSprite()->Flush();

			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->SetRenderState(D3DRS_SRCBLEND, SourceBlend);
			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->SetRenderState(D3DRS_DESTBLEND, DestBlend);

			for(unsigned int i = 0; i < MaxParticles; i++)
			{
				if(Particles[i]->isAlive == true)
				{
					if(Particles[i]->haslife == true)
					{
						if(ImageID != -1)
						{
							CSGD_TextureManager::GetInstance()->Draw(ImageID, (int)Particles[i]->PosX, (int)Particles[i]->PosY, Particles[i]->CurScaleX,
							Particles[i]->CurScaleY, nullptr, (CSGD_TextureManager::GetInstance()->GetTextureWidth(ImageID)/2.0f),
							(CSGD_TextureManager::GetInstance()->GetTextureHeight(ImageID)/2.0f), 0.0f, Particles[i]->CurColor);
						}
					}
				}
			}

			CSGD_Direct3D::GetInstance()->GetSprite()->Flush();

			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->SetRenderState(D3DRS_SRCBLEND, temp[0]);
			CSGD_Direct3D::GetInstance()->GetDirect3DDevice()->SetRenderState(D3DRS_DESTBLEND, temp[1]);
		}

		void Update(float ElapsedTime)
		{
			for(unsigned int i = 0; i < MaxParticles; i++)
			{
				if(Particles[i]->haslife == false)
				{
					if (MinLife > MaxLife)
					{
						MinLife = 0;
					}

					Particles[i]->CurLife = (float)(rand() % (int)((MaxLife - MinLife + 1) - MinLife));
					Particles[i]->CurScaleX = ScaleX;
					Particles[i]->CurScaleY = ScaleY;
					Particles[i]->CurColor = StartColor;
					Particles[i]->CurVelX = VelX;
					Particles[i]->CurVelY = VelY;
					Particles[i]->isAlive = true;
					Particles[i]->haslife = true;
					Particles[i]->JustSpawned = true;


					float RectX = (float)(EmitterRect.right - EmitterRect.left);
					float RectY = (float)(EmitterRect.bottom - EmitterRect.top);

					if(RectX == 0)
						RectX = 1;
					else
						RectX = (float)((rand() % (int)((RectX + 1)) + EmitterRect.left));
				
					if(RectY == 0)
						RectY = 1;
					else
					{
						RectY = (float)((rand() % (int)((RectY + 1)) + EmitterRect.top));
					}
					Particles[i]->PosX = (float)RectX;
					Particles[i]->PosY = (float)RectY;
				}
			}

			for(unsigned int i = 0; i < MaxParticles; i++)
			{
				if(Particles[i]->isAlive == true)
				{
					if(Particles[i]->JustSpawned == true && looping == true)
					{
						TimeBucket += ElapsedTime;

						if(TimeBucket >= 3.0f)
						{
							Particles[i]->JustSpawned = false;
							TimeBucket = 0.0f;
						}
					}
					else
					{
						Particles[i]->CurLife += ElapsedTime;
						Particles[i]->CurColor.r =  Lerp(StartColor.r, EndColor.r, (Particles[i]->CurLife/MaxLife));
						Particles[i]->CurColor.g =  Lerp(StartColor.g, EndColor.g, (Particles[i]->CurLife/MaxLife));
						Particles[i]->CurColor.b =  Lerp(StartColor.b, EndColor.b, (Particles[i]->CurLife/MaxLife));
						Particles[i]->CurColor.a =  Lerp(StartColor.a, EndColor.a, (Particles[i]->CurLife/MaxLife));
					
						Particles[i]->CurVelX = Lerp(VelX, EndVelX, (Particles[i]->CurLife/MaxLife)); //VelX*ElapsedTime;
						Particles[i]->CurVelY = Lerp(VelY, EndVelY, (Particles[i]->CurLife/MaxLife)); //VelY*ElapsedTime;

						Particles[i]->CurScaleX = Lerp(ScaleX, EndScaleX, (Particles[i]->CurLife/MaxLife));
						Particles[i]->CurScaleY = Lerp(ScaleY, EndScaleY, (Particles[i]->CurLife/MaxLife));

						Particles[i]->PosX += Particles[i]->CurVelX*ElapsedTime;
						Particles[i]->PosY += Particles[i]->CurVelY*ElapsedTime;

						if(Particles[i]->CurLife >= MaxLife)
						{
							Particles[i]->isAlive = false;

							if(looping == true)
							{
								Reset(Particles[i]);
								Particles[i]->haslife = false;
							}
						}
					}
				}

				if (looping == true)
                {
                    Particles[i]->isAlive = true;
                }
			}
		}

		void Reset(Particle* P)
		{
			if (MinLife > MaxLife)
			{
				MinLife = 0;
			}

			P->CurLife = (float)(rand() % (int)((MaxLife - MinLife + 1) - MinLife));
			P->CurScaleX = ScaleX;
			P->CurScaleY = ScaleY;
			P->CurColor = StartColor;
			P->CurVelX = VelX;
			P->CurVelY = VelY;
			P->isAlive = true;
			
			float RectX = (float)(EmitterRect.right - EmitterRect.left);
			float RectY = (float)(EmitterRect.bottom - EmitterRect.top);

				if(RectX == 0)
					RectX = 1;
				else
					RectX = (float)((rand() % (int)((EmitterRect.right - EmitterRect.left + 1)) + EmitterRect.left));
				
				if(RectY == 0)
					RectY = 1;
				else
					RectY = (float)((rand() % (int)((EmitterRect.bottom - EmitterRect.top + 1)) + EmitterRect.top));

			P->PosX = RectX;
			P->PosY = RectY;
		}


		void ResetAll(void)
		{
			if (MinLife > MaxLife)
			{
				MinLife = 0;
			}

			for(unsigned int i = 0; i < MaxParticles; i++)
			{
				Particles[i]->CurLife = (float)(rand() % (int)((MaxLife - MinLife + 1) - MinLife));
				Particles[i]->CurScaleX = ScaleX;
				Particles[i]->CurScaleY = ScaleY;
				Particles[i]->CurColor = StartColor;
				Particles[i]->CurVelX = VelX;
				Particles[i]->CurVelY = VelY;
				Particles[i]->isAlive = true;
			
				float RectX = (float)(EmitterRect.right - EmitterRect.left);
				float RectY = (float)(EmitterRect.bottom - EmitterRect.top);

					if(RectX == 0)
						RectX = 1;
					else
						RectX = (float)((rand() % (int)((EmitterRect.right - EmitterRect.left + 1)) + EmitterRect.left));
				
					if(RectY == 0)
						RectY = 1;
					else
						RectY = (float)((rand() % (int)((EmitterRect.bottom - EmitterRect.top + 1)) + EmitterRect.top));

				Particles[i]->PosX = RectX;
				Particles[i]->PosY = RectY;
			}
		}

		//Accessors		 
		int			GetImageID(void)		{ return ImageID;		}
		int			GetSourceBlend(void)	{ return SourceBlend;	}	
		int			GetDestBlend(void)		{ return DestBlend;		}
		RECT		GetRect(void)			{ return EmitterRect;	}
		float		GetMaxLife(void)		{ return MaxLife;		}	
		float		GetMinLife(void)		{ return MinLife;		}
		bool		GetLooping(void)		{ return looping;		}	
		float		GetVelX(void)			{ return VelX;			}	
		float		GetVelY(void)			{ return VelY;			}		
		D3DXCOLOR	GetStartColor(void)		{ return StartColor;	}	
		D3DXCOLOR	GetEndColor(void)		{ return EndColor;		}	
		float		GetScaleX(void)			{ return ScaleX;		}	
		float		GetScaleY(void)			{ return ScaleY;		}		
		float		GetMaxParticles(void)	{ return MaxParticles;	}
		float		GetEndScaleX(void)		{ return EndScaleX;		}
		float		GetEndScaleY(void)		{ return EndScaleY;		}
		float		GetEndVelX(void)		{ return EndVelX;		}
		float		GetEndVelY(void)		{ return EndVelY;		}
		Particle**	GetParticles(void)		{ return Particles;		}

		//Mutators
		void SetImageID(int ID)					{ ImageID = ID;			}
		void SetSourceBlend(int SrcBld)			{ SourceBlend = SrcBld;	}
		void SetDestBlend(int DstBld)			{ DestBlend = DstBld;	}
		void SetMaxLife(float ML)				{ MaxLife =	ML;			}
		void SetLooping(bool loop)				{ looping = loop;		}
		void SetStartColorR(float SCred)		{ StartColor.r = SCred;	}
		void SetStartColorG(float SCgreen)		{ StartColor.g = SCgreen;}
		void SetStartColorB(float SCblue)		{ StartColor.b = SCblue;}
		void SetStartColorA(float SCalpha)		{ StartColor.a = SCalpha;}
		void SetEndColorR(float ECred)			{ EndColor.r = ECred;	}
		void SetEndColorG(float ECgreen)		{ EndColor.g = ECgreen; }
		void SetEndColorB(float ECblue)			{ EndColor.b = ECblue;	}
		void SetEndColorA(float ECalpha)		{ EndColor.a = ECalpha;	}
		void SetMaxParticles(float MP)			{ MaxParticles = MP;	}
		void SetVelX(float X)					{ VelX = X;				}
		void SetVelY(float Y)					{ VelY = Y;				}
		void SetScaleX(float X)					{ ScaleX = X;			}
		void SetScaleY(float Y)					{ ScaleY = Y;			}
		void SetEndVelX(float X)				{ EndVelX = X;			}
		void SetEndVelY(float Y)				{ EndVelY = Y;			}
		void SetEndScaleX(float X)				{ EndScaleX = X;		}
		void SetEndScaleY(float Y)				{ EndScaleY = Y;		}
		void SetRectBottom(LONG rectBot)		{ EmitterRect.bottom = rectBot;}
		void SetRectRight(LONG rectRight)		{ EmitterRect.right = rectRight;}
		void SetRectLeft(LONG rectLeft)			{ EmitterRect.left = rectLeft;}
		void SetRectTop(LONG rectTop)			{ EmitterRect.top = rectTop; }
		void SetMinLife(float minlife)			{ MinLife = minlife;	}
		void SetParticles(unsigned int count)
		{
			Particles = new Emitter::Particle*[count];

			for(unsigned int i = 0; i < count; i++)
			{
				Particles[i] = new Particle();
			}
		}

		void ClearParticles(void)
		{
			for(unsigned int i = 0; i < MaxParticles; i++)
			{
				delete Particles[i];
				Particles[i] = nullptr;
			}

			delete Particles;
			Particles = nullptr;
		}
	};

	vector<string>		  EmitterKeys;
	map<string, Emitter*> EmitterMap;

public:
	static ParticleManager* GetInstance(void);
	void ShutDown(void);
	void DeleteInstance(void);
	void Render(string FileName);
	void Update(float ElapsedTime, string KeyName);
	void Update(float ElapsedTime);
	bool Load(const char* FileName);
	void Reset(string FileName);
	void ResetParticles(string FileName);

	vector<string>	GetKeys(void)					    {return EmitterKeys;}
	void			SetKeys(vector<string> emitterkeys) {EmitterKeys = emitterkeys;}
	map<string, Emitter*> GetMap(void)					{return EmitterMap;}
};